iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 6
1
Modern Web

Framework Surfing 30天系列 第 6

Day 6 - React with Apollo - part 1

  • 分享至 

  • xImage
  •  

用 create-react-app 初始化一個專案

$ create-react-app react-graphql

安裝需要的套件

  • graphql
  • apollo-boost
  • @apollo/react-hooks

$ npm install --save graphql apollo-boost @apollo/react-hooks

設定 apollo client

index.js 裡面,加上 ApolloClient 以及 ApolloProvider

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import ApolloClient from 'apollo-boost'
import { ApolloProvider } from '@apollo/react-hooks';

const client = new ApolloClient({
  uri: 'http://localhost:7000/graphql',
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>, 
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

寫第一個 Query

$ mkdir src/graphql

$ touch src/graphql/category.js

這邊用 import { gql } from 'graphql-tag' 而不用 import { gql } from 'apollo-boost' 是因為考慮到檔案大小的問題,

import { gql } from 'graphql-tag';

export const GET_CATEGORY_LIST = gql`
    query GetCategoryList {
        categories {
            id
            name
        }
    }
`

用 Query 跟 API Server 拿資料

直接在 App.js 測試,將剛剛寫好的 GET_CATEGORY_LIST 引入,然後用 useQuery 來使用它

import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import { GET_CATEGORY_LIST } from './graphql/category';

function App() {
  const { loading, error, data } = useQuery(GET_CATEGORY_LIST);

  if (loading) return <div className="App">loading...</div>

  return (
    <div className="App">
      { data ? JSON.stringify(data) : ''}
    </div>
  );
}

export default App;

測試一下有沒有拿到資料,首先要先打開我們的 API Server

$ cd go-gql-server

$ scripts/run.sh

然後將我們的前端打開

$ yarn start

用瀏覽器打開 localhost:3000,應該要能看到 Server 傳給我們的 Categories ,但是卻沒辦法,打開 console 看到是 CORS 的問題,那就來處理一下吧

處理 CORS

因為我們的後端是選用 Gin,所以可以直接用這個套件 https://github.com/gin-contrib/cors

$ cd go-gql-server

$ go get github.com/gin-contrib/cors

然後到我們的 pkg/server/main.go

package server

import (
	log "log"
	"time"

	"github.com/wtlin1228/go-gql-server/internal/orm"

+	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/wtlin1228/go-gql-server/internal/handlers"
	"github.com/wtlin1228/go-gql-server/pkg/utils"
)

var host, port, gqlPath, gqlPgPath string
var isPgEnabled bool

func init() {
	host = utils.MustGet("GQL_SERVER_HOST")
	port = utils.MustGet("GQL_SERVER_PORT")
	gqlPath = utils.MustGet("GQL_SERVER_GRAPHQL_PATH")
	gqlPgPath = utils.MustGet("GQL_SERVER_GRAPHQL_PLAYGROUND_PATH")
	isPgEnabled = utils.MustGetBool("GQL_SERVER_GRAPHQL_PLAYGROUND_ENABLED")
}

// Run spins up the server
func Run(orm *orm.ORM) {
	log.Println("GORM_CONNECTION_DSN: ", utils.MustGet("GORM_CONNECTION_DSN"))

	endpoint := "http://" + host + ":" + port

	r := gin.Default()

+	r.Use(cors.New(cors.Config{
+		AllowOriginFunc:  func(origin string) bool { return origin == "http://localhost:3000" },
+		AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "PATCH"},
+		AllowHeaders:     []string{"Origin", "Content-Length", "Content-Type"},
+		AllowCredentials: true,
+		MaxAge:           12 * time.Hour,
+	}))

	// Handlers
	// Simple keep-alive/ping handler
	r.GET("/heartbeat", handlers.Heartbeat())

	// GraphQL handlers
	// Playground handler
	if isPgEnabled {
		r.GET(gqlPgPath, handlers.PlaygroundHandler(gqlPath))
		log.Println("GraphQL Playground @ " + endpoint + gqlPgPath)
	}
	// Pass in the ORM instance to the GraphqlHandler
	r.POST(gqlPath, handlers.GraphqlHandler(orm))
	log.Println("GraphQL @ " + endpoint + gqlPath)

	// Run the server
	// Inform the user where the server is listening
	log.Println("Running @ " + endpoint)
	// Print out and exit(1) to the OS if the server cannot run
	log.Fatal(r.Run(host + ":" + port))
}

設定好之後就可以用囉!

那 Mutation 要怎麼寫呢?

在 React 的世界,我們都是先將 Form 的資料存在 state,然後按下送出的時候直接從 state 拿 Form 的資料,所以會需要可以將參數傳進 mutation 裡面,這時候就要用到 variables,下面這個例子就是能夠傳入一個 name 當參數的 mutation。

const CREATE_CATEGORY = gql`
  mutation CreateCategory( 
    $name: String!
  ) {
    createCategory(input: {
      name: $name
    }) {
      id
      name
      errors {
        field
        messages
      }
    }
  }
`

用法如下

import React, { useState } from 'react';
import { useMutation } from '@apollo/react-hooks';
import { GET_CATEGORY_LIST, CREATE_CATEGORY } from './graphql/category';

function App() {
	const { loading, error, data } = useQuery(GET_CATEGORY_LIST);
  const [name, setName] = useState('');
	const [createCategory, { data: mutationData }] = useMutation(CREATE_CATEGORY);
  
	function handleSubmit(e) {
    e.preventDefault();

    createCategory({ 
      variables: { name },
      refetchQueries: ['GetCategoryList']
    });
  }

	if (loading) return <div className="App">loading...</div>

	return (
    <div className="App">
      <form onSubmit={handleSubmit}>
        <input value={name} onChange={e => setName(e.target.value)}/>
        <button type="submit">Create Category</button>
      </form>
    </div>
  );
}

export default App;

上一篇
Day 5 - 設計前端頁面 - part 1
系列文
Framework Surfing 30天6
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言